﻿using Common.Http.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace Common.Http
{
    public class HttpApiClient : HttpClient
    {
        public HttpApiClient(string baseAddress)
            : base(new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.GZip, UseProxy = false, UseCookies = false, PreAuthenticate = false })
        {

            //this.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            this.BaseAddress = new Uri(baseAddress);
            this.DefaultRequestHeaders.Connection.Add("keep-alive");
            var msg = this.SendAsync(new HttpRequestMessage
            {
                Method = new System.Net.Http.HttpMethod("HEAD"),
                RequestUri = new Uri(baseAddress + "/")
            }).Result.EnsureSuccessStatusCode();

            Common.Log.LogHelper.Info(string.Format("初始化http:{0}-{1}", baseAddress, msg.StatusCode));
        }

        #region Post

        public async Task<string> DoPostAsync(string url, IEnumerable<KeyValuePair<string, string>> param = null)
        {
            HttpContent content = null;
            if (param != null)
            {
                content = new FormUrlEncodedContent(param);
            }
            using (var response = await this.PostAsync(url, content))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoPostAsync(url, param);
                }
                if (response.IsSuccessStatusCode)
                {
                    return await response.Content.ReadAsStringAsync();
                }
                return null;
            }
        }

        public async Task<TResult> DoPostAsync<TResult>(string url, IEnumerable<KeyValuePair<string, string>> param = null)
        {
            HttpContent content = null;
            if (param != null)
            {
                content = new FormUrlEncodedContent(param);
            }
            using (var response = await this.PostAsync(url, content))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoPostAsync<TResult>(url, param);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsAsync<TResult>();
                    return readRes;
                }
                else
                {
                    return default(TResult);
                }
            }
        }

        public async Task<TResult> DoPostAsJsonAsync<TParam, TResult>(string url, TParam param)
            where TParam : class
        {
            using (var response = await this.PostAsJsonAsync(url, param))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoPostAsJsonAsync<TParam, TResult>(url, param);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsAsync<TResult>();
                    return readRes;
                }
                else
                {
                    return default(TResult);
                }
            }
        }


        public async Task<string> DoPostAsJsonAsync<TParam>(string url, TParam param)
            where TParam : class
        {
            using (var response = await this.PostAsJsonAsync(url, param))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoPostAsJsonAsync<TParam>(url, param);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsStringAsync();
                    return readRes;
                }
                else
                {
                    return null;
                }
            }
        }

        public async Task<TResult> DoPostAsync<TParam, TResult>(string url, TParam param)
            where TParam : class
        {
            HttpContent content = null;
            if (param != null)
            {
                content = new StringContent(param.ToParams());
                content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
            }
            using (var response = await this.PostAsync(url, content))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoPostAsync<TParam, TResult>(url, param);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsAsync<TResult>();
                    return readRes;
                }
                else
                {
                    return default(TResult);
                }
            }
        }

        public async Task<TResult> DoUploadAsync<TResult>(string url, object param, params FileUpload[] files)
        {
            HttpContent content = null;
            if (param != null)
            {
                var multipart = new MultipartFormDataContent();
                if (param != null)
                {
                    var tt = param.GetType();
                    object value;
                    foreach (var p in tt.GetProperties())
                    {
                        value = p.GetValue(param);
                        if (value == null)
                            continue;
                        multipart.Add(new StringContent(value.ToString()), p.Name);
                    }
                }
                if (files != null)
                {
                    foreach (var file in files)
                    {
                        content = new ByteArrayContent(file.FileData);
                        if (file.ContentType != null)
                        {
                            content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(file.ContentType);
                        }
                        multipart.Add(content, file.FileFormName, file.FileName);
                    }
                }
                content = multipart;
            }
            using (var response = await this.PostAsync(url, content))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoUploadAsync<TResult>(url, param, files);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsAsync<TResult>();
                    return readRes;
                }
                else
                {
                    return default(TResult);
                }
            }
        }
        #endregion

        #region Put

        public async Task<string> DoPutAsync(string url, IEnumerable<KeyValuePair<string, string>> param = null)
        {
            var content = new FormUrlEncodedContent(param);
            using (var response = await this.PutAsync(url, content))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoPutAsync(url, param);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsStringAsync();
                    return readRes;
                }
                else
                {
                    return null;
                }
            }
        }

        public async Task<TResult> DoPutAsJsonAsync<TParam, TResult>(string url, TParam param)
        {
            using (var response = await this.PutAsJsonAsync<TParam>(url, param))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoPutAsJsonAsync<TParam, TResult>(url, param);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsAsync<TResult>();
                    return readRes;
                }
                else
                {
                    return default(TResult);
                }
            }
        }

        public async Task<TResult> DoPutAsync<TResult>(string url, IEnumerable<KeyValuePair<string, string>> param = null)
        {
            var content = new FormUrlEncodedContent(param);
            using (var response = await this.PutAsync(url, content))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoPutAsync<TResult>(url, param);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsAsync<TResult>();
                    return readRes;
                }
                else
                {
                    return default(TResult);
                }
            }
        }
        #endregion

        #region Get

        public async Task<string> DoGetAsync(string url, IEnumerable<KeyValuePair<string, string>> param = null)
        {
            if (param != null)
            {
                url = url.Contains('?') ? string.Format(@"{0}&{1}", url, param.ToParams()) : string.Format(@"{0}?{1}", url, param.ToParams());
            }
            using (var response = await this.GetAsync(url))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoGetAsync(url);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsStringAsync();
                    return readRes;
                }
                else
                {
                    return null;
                }
            }
        }

        public async Task<TResult> DoGetAsync<TResult>(string url, IEnumerable<KeyValuePair<string, string>> param = null)
        {
            if (param != null)
            {
                url = url.Contains('?') ? string.Format(@"{0}&{1}", url, param.ToParams()) : string.Format(@"{0}?{1}", url, param.ToParams());
            }
            using (var response = await this.GetAsync(url))
            {

                if (await DoRequestAgain(response))
                {
                    return await DoGetAsync<TResult>(url);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsAsync<TResult>();
                    return readRes;
                }
                else
                {
                    return default(TResult);
                }
            }
        }

        public async Task<TResult> DoGetAsync<TResult>(string url, object param)
        {
            if (param != null)
            {
                url = url.Contains('?') ? string.Format(@"{0}&{1}", url, param.ToParams()) : string.Format(@"{0}?{1}", url, param.ToParams());
            }
            using (var response = await this.GetAsync(url))
            {

                if (await DoRequestAgain(response))
                {
                    return await DoGetAsync<TResult>(url);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsAsync<TResult>();
                    return readRes;
                }
                else
                {
                    return default(TResult);
                }
            }
        }
        #endregion

        #region Delete

        public async Task<string> DoDeleteAsync(string url, IEnumerable<KeyValuePair<string, string>> param = null)
        {
            HttpContent content = null;
            if (param != null)
            {
                content = new FormUrlEncodedContent(param);
            }
            var request = new HttpRequestMessage(System.Net.Http.HttpMethod.Delete, url)
            {
                Content = content
            };
            using (var response = await this.SendAsync(request))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoDeleteAsync(url, param);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsStringAsync();
                    return readRes;
                }
                else
                {
                    return null;
                }
            }
        }

        public async Task<TResult> DoDeleteAsync<TResult>(string url, IEnumerable<KeyValuePair<string, string>> param = null)
        {
            HttpContent content = null;
            if (param != null)
            {
                content = new FormUrlEncodedContent(param);
            }
            var request = new HttpRequestMessage(System.Net.Http.HttpMethod.Delete, url)
            {
                Content = content
            };
            using (var response = await this.SendAsync(request))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoDeleteAsync<TResult>(url, param);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsAsync<TResult>();
                    return readRes;
                }
                else
                {
                    return default(TResult);
                }
            }
        }

        public async Task<TResult> DoDeleteAsync<TParam, TResult>(string url, TParam param)
            where TParam : class
        {
            HttpContent content = null;
            if (param != null)
            {
                content = new StringContent(param.ToParams());
                content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
            }
            var request = new HttpRequestMessage(System.Net.Http.HttpMethod.Delete, url)
            {
                Content = content
            };
            using (var response = await this.SendAsync(request))
            {
                if (await DoRequestAgain(response))
                {
                    return await DoDeleteAsync<TParam, TResult>(url, param);
                }
                if (response.IsSuccessStatusCode)
                {
                    var readRes = await response.Content.ReadAsAsync<TResult>();
                    return readRes;
                }
                else
                {
                    return default(TResult);
                }
            }
        }
        #endregion

        protected virtual async Task<bool> DoRequestAgain(HttpResponseMessage response)
        {
            return false;
        }



    }
}
